package com.googlecode.rulio.examples;

import com.googlecode.rulio.*;
import com.googlecode.rulio.Predicate;
import com.googlecode.rulio.rendering.html.HtmlExecutionReportRenderer;
import com.googlecode.yadic.Container;
import com.googlecode.yadic.SimpleContainer;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

import static com.googlecode.rulio.Rules.action;
import static com.googlecode.rulio.Rules.conditionalAction;
import static com.googlecode.rulio.examples.LogDogToFileAction.logToFile;
import static com.googlecode.rulio.predicates.Predicates.*;

import static java.util.Arrays.asList;


public class $Examples {
    public static void main(String... args) throws Exception {
        Rules<Object> rules = new Rules<Object>(yadicActivator(new ServiceDependency()), false)
                // Type safety derived from predicate
//                                .add(ofType(String.class), animalAction()) // Should not compile
                //                .add(ofType(Dog.class), stringOnlyAction())   // Should not compile
//                                .add(ofType(Cat.class), dogOnlyAction())   // Should not compile
                .add(ofType(String.class), stringOnlyAction())
                .add(ofType(Animal.class), animalAction(), animalAction())
                .add(ofType(Cat.class), catOnlyAction())
                .add(ofType(Dog.class), animalAction())
                .add(ofType(Dog.class), dogOnlyAction())
                .add(isDog(), dogOnlyAction())
                .add(hasType("Cat"), animalAction())


                        // Multiple actions
                .add(isDog(), dogOnlyAction(), animalAction())

                        // Chained predicates
                .add(ofType(Dog.class).and(hasType("Dog")), dogOnlyAction())

                        //TODO ?? Fix below
//                .add(ofType(Animal.class).and(not(ofType(Cat.class))), animalAction())

                        // Activating actions
                .add(isDog(), action(LogDogToFileAction.class, new LogFilePath("dogs.log")))

                        // Conditional  actions
                .add(ofType(Dog.class),
                        conditionalAction(ofType(Cat.class), logToFile(new LogFilePath("dogs.log"))),
                        conditionalAction(ofType(Dog.class), dogOnlyAction()))


                        // Method on actions class pattern
                .add(isDog(), logToFile(new LogFilePath("dogs.log"), new ServiceDependency())) // Static dependencies
                .add(isDog(), logToFile(new LogFilePath("dogs.log"))) // Activating actions with method call from the class
                .add(ofType(Dog.class),
                        logToFile(new LogFilePath("dogs.log")),
                        logToFile(new LogFilePath("AllDogs.log"), new ServiceDependency())) // Multiple actions activation


                .add(isDog(), new Rules<Dog>(true)
                        .add(isDog(), logToFile(new LogFilePath("dogs.log")), logToFile(new LogFilePath("animals.log")), dogOnlyAction())
                        .add(ofType(Dog.class), animalAction())
                        .add(ofType(Dog.class).and(hasType("AAA")), animalAction()),
                        logToFile(new LogFilePath("log.txt")))


                .add(ofType(Dog.class).and(hasType("DOES NOT MATCH")), new Rules<Dog>(true)
                        .add(isDog(), logToFile(new LogFilePath("dogs.log")), logToFile(new LogFilePath("animals.log")), dogOnlyAction())
                        .add(ofType(Dog.class), animalAction())
                        .add(ofType(Dog.class).and(hasType("AAA")), animalAction()),
                        logToFile(new LogFilePath("log.txt")));


        // Execution report and rendering
        ExecutionReport report = rules.execute(new Dog());


        File file = new File("rendered.html");
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(file));
        outputStreamWriter.write(new HtmlExecutionReportRenderer().render(report));
        outputStreamWriter.flush();
        outputStreamWriter.close();

        System.out.println(file.getAbsolutePath());
    }


    private static void printClassDetails(Class<?> aClass) {
        StringBuilder builder = new StringBuilder();
        builder.append("   Canonical name: ").append(aClass.getCanonicalName()).append("\n");
        builder.append("   Component type: ").append(aClass.getComponentType()).append("\n");
        builder.append("   Declaring class: ").append(aClass.getDeclaringClass()).append("\n");
        builder.append("   Enclosing class: ").append(aClass.getEnclosingClass()).append("\n");
        builder.append("   Enclosing method: ").append(aClass.getEnclosingMethod()).append("\n");
        builder.append("   Enclosing constructor: ").append(aClass.getEnclosingConstructor()).append("\n");
        builder.append("   Superclass: ").append(aClass.getSuperclass()).append("\n");
        builder.append("   Type parameters: ").append(asList(aClass.getTypeParameters())).append("\n");
        builder.append("   Is local class: ").append(aClass.isLocalClass()).append("\n");
        builder.append("   Is member class: ").append(aClass.isMemberClass()).append("\n");
        builder.append("   Is synthetic class: ").append(aClass.isSynthetic()).append("\n");
        builder.append("   Generic superclass: ").append(aClass.getGenericSuperclass()).append("\n");
        builder.append("   Generic interfaces: ").append(asList(aClass.getGenericInterfaces())).append("\n");
        builder.append("   Declared classes: ").append(asList(aClass.getDeclaredClasses())).append("\n");
        builder.append("   Classes: ").append(asList(aClass.getClasses())).append("\n");
        System.out.println(builder.toString());
    }

    private static Action<Animal, Void> nestedRuleAction() {
        return new Action<Animal, Void>() {
            public Void execute(Animal value) {
                return null;
            }


            @Override
            public String toString() {
                return "nested rule action";
            }
        };
    }

    public static Predicate<Animal> hasType(final String type) {
        return new Predicate<Animal>() {
            public boolean matches(Animal value) {
                return value.type().equalsIgnoreCase(type);
            }

            @Override
            public String toString() {
                return "hasType(" + type + ")";
            }
        };
    }


    static Predicate<Dog> isDog() {
        return new Predicate<Dog>() {
            public boolean matches(Dog value) {
                return "Dog".equalsIgnoreCase(value.type());
            }

            @Override
            public String toString() {
                return "isDog";
            }
        };


    }


    private static Action<String, Void> stringOnlyAction() {
        return new Action<String, Void>() {
            public Void execute(String value) {
                return null;
            }

            @Override
            public String toString() {
                return "String action";
            }
        };
    }


    static Action<Animal, String> animalAction() {
        return new Action<Animal, String>() {
            public String execute(Animal value) {
                return "This is a result of " + getClass().getEnclosingMethod();
            }

            @Override
            public String toString() {
                return "Animal action";
            }
        };
    }


    static Action<Dog, Void> dogOnlyAction() {
        return new Action<Dog, Void>() {
            public Void execute(Dog value) {
                return null;
            }

            @Override
            public String toString() {
                return "Dog only actions";
            }
        };
    }


    static Action<Cat, Void> catOnlyAction() {
        return new Action<Cat, Void>() {
            public Void execute(Cat value) {
                return null;
            }

            @Override
            public String toString() {
                return "Cat only actions";
            }
        };
    }


    @SuppressWarnings("unchecked")
    private static ActionActivator yadicActivator(final Object... dependencies) {
        return new ActionActivator() {
            @Override
            public <T extends Action> T activate(Class<T> actionClass, Object... scopeDependencies) throws Exception {
                Container container = new SimpleContainer();


                for (Object dependency : dependencies) {
                    Class<Object> aClass = (Class<Object>) dependency.getClass();
                    container.addInstance(aClass, dependency);
                }

                for (Object scopeDependency : scopeDependencies) {
                    Class<Object> aClass = (Class<Object>) scopeDependency.getClass();
                    container.addInstance(aClass, scopeDependency);
                }

                return container.create(actionClass);
            }
        };
    }
}





